﻿using System;
using System.Collections.Generic;
using BReusable.StaticReflection;

namespace BReusable.Containers
{
	/// <summary>
	/// Dictionary that enforces key=func(value)
	/// </summary>
	/// <typeparam name="TKey"></typeparam>
	/// <typeparam name="TValue"></typeparam>
	public class LambdaDictionary<TKey, TValue> : IDictionary<TKey, TValue>
	{
		private readonly IEqualityComparer<TKey> _comparer;
		private readonly Func<TValue, TKey> _getKeyFunc;
		private readonly IDictionary<TKey, TValue> _dictionary;


		public LambdaDictionary(Func<TValue, TKey> lookup, IEqualityComparer<TKey> comparer = null
			, IEnumerable<TValue> items = null)
		{
			_getKeyFunc = lookup;
			_comparer = comparer ?? EqualityComparer<TKey>.Default;
			Guard.IsNotNullIL<IEqualityComparer<TKey>>(() => _comparer);
			_dictionary = new Dictionary<TKey, TValue>(_comparer);
			if (items != null)
				foreach (var item in items)
				{
					Add(item);
				}
		}


		private void GuardKeys(TKey passedKey, TKey generatedKey)
		{
			if (_comparer.Equals(passedKey, generatedKey) == false)
				throw new ArgumentException("Invalid Key:" + passedKey.ToString());
		}
		public TKey GetKey(TValue value)
		{
			return _getKeyFunc(value);
		}


		public ImmutableDictionary<TKey, TValue> ToImmutable()
		{
			return new ImmutableDictionary<TKey, TValue>(this);
		}
		#region IDictionary<TKey,TValue> Members


		public void Add(TValue value)
		{
			_dictionary.Add(_getKeyFunc(value), value);
		}

		public void Add(TKey key, TValue value)
		{
			var localKey = _getKeyFunc(value);
			GuardKeys(key, localKey);
			_dictionary.Add(localKey, value);
		}

		public bool ContainsKey(TKey key)
		{
			return _dictionary.ContainsKey(key);
		}

		public ICollection<TKey> Keys
		{
			get { return _dictionary.Keys; }
		}

		public bool Remove(TKey key)
		{
			return _dictionary.Remove(key);
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			return _dictionary.TryGetValue(key, out value);
		}

		public ICollection<TValue> Values
		{
			get
			{
				return _dictionary.Values;
			}
		}

		public TValue this[TKey key]
		{
			get
			{
				return _dictionary[key];
			}
			set
			{
				var localKey = _getKeyFunc(value);
				GuardKeys(key, localKey);
				_dictionary[localKey] = value;
			}
		}

		#endregion

		#region ICollection<KeyValuePair<TKey,TValue>> Members

		public void Add(KeyValuePair<TKey, TValue> item)
		{
			GuardKeys(item.Key, _getKeyFunc(item.Value));
			_dictionary.Add(item);
		}

		public void Clear()
		{
			_dictionary.Clear();
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			return _dictionary.Contains(item);
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
			_dictionary.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get { return _dictionary.Count; }
		}

		public bool IsReadOnly
		{
			get { return _dictionary.IsReadOnly; }
		}

		public bool Remove(KeyValuePair<TKey, TValue> item)
		{
			return _dictionary.Remove(item);
		}

		#endregion

		#region IEnumerable<KeyValuePair<TKey,TValue>> Members

		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			return _dictionary.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _dictionary.GetEnumerator();
		}

		#endregion
	}

}
